// 版权所有 Epic Games, Inc. 保留所有权利。

#include "LyraDevelopmentStatics.h"
#include "AssetRegistry/ARFilter.h"
#include "AssetRegistry/AssetData.h"
#include "Development/LyraDeveloperSettings.h"
#include "Engine/Blueprint.h"
#include "Engine/Engine.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Engine/World.h"
#include "Misc/PackageName.h"
#include "Modules/ModuleManager.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraDevelopmentStatics)

/**
 * 是否应该直接跳转到游戏玩法
 */
bool ULyraDevelopmentStatics::ShouldSkipDirectlyToGameplay()
{
#if WITH_EDITOR
	if (GIsEditor) // 检查是否在编辑器中
	{
		return !GetDefault<ULyraDeveloperSettings>()->bTestFullGameFlowInPIE; // 返回是否不测试完整游戏流程
	}
#endif
	return false; // 默认返回false
}

/**
 * 是否应该加载装饰性背景
 */
bool ULyraDevelopmentStatics::ShouldLoadCosmeticBackgrounds()
{
#if WITH_EDITOR
	if (GIsEditor) // 检查是否在编辑器中
	{
		return !GetDefault<ULyraDeveloperSettings>()->bSkipLoadingCosmeticBackgroundsInPIE; // 返回是否不跳过加载装饰性背景
	}
#endif
	return true; // 默认返回true
}

/**
 * 玩家机器人是否可以攻击
 */
bool ULyraDevelopmentStatics::CanPlayerBotsAttack()
{
#if WITH_EDITOR
	if (GIsEditor) // 检查是否在编辑器中
	{
		return GetDefault<ULyraDeveloperSettings>()->bAllowPlayerBotsToAttack; // 返回是否允许玩家机器人攻击
	}
#endif
	return true; // 默认返回true
}

//@TODO: 实际上希望接受一个lambda并在大多数情况下在每个权限世界上运行...
/**
 * 查找编辑器内播放权限世界
 */
UWorld* ULyraDevelopmentStatics::FindPlayInEditorAuthorityWorld()
{
	check(GEngine); // 检查GEngine是否有效

	// 查找服务器世界（任何PIE世界都可以，如果它们在没有专用服务器的情况下运行，但专用服务器世界是理想的）
	UWorld* ServerWorld = nullptr; // 初始化服务器世界为空
#if WITH_EDITOR
	for (const FWorldContext& WorldContext : GEngine->GetWorldContexts()) // 遍历所有世界上下文
	{
		if (WorldContext.WorldType == EWorldType::PIE) // 检查是否为PIE世界
		{
			if (UWorld* TestWorld = WorldContext.World()) // 获取世界
			{
				if (WorldContext.RunAsDedicated) // 检查是否作为专用服务器运行
				{
					// 理想情况
					ServerWorld = TestWorld; // 设置为服务器世界
					break; // 跳出循环
				}
				else if (ServerWorld == nullptr) // 如果服务器世界为空
				{
					ServerWorld = TestWorld; // 设置为服务器世界
				}
				else
				{
					// 我们已有一个候选，查看这个是否"更好"
					if (TestWorld->GetNetMode() < ServerWorld->GetNetMode()) // 比较网络模式
					{
						return ServerWorld; // 返回服务器世界
					}
				}
			}
		}
	}
#endif

	return ServerWorld; // 返回服务器世界
}

/**
 * 获取所有蓝图
 */
TArray<FAssetData> ULyraDevelopmentStatics::GetAllBlueprints()
{
	FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")); // 加载资源注册表模块

	FName PluginAssetPath; // 插件资源路径

	TArray<FAssetData> BlueprintList; // 蓝图列表
	FARFilter Filter; // 资源注册表过滤器
	Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName()); // 添加蓝图类路径
	Filter.bRecursivePaths = true; // 递归路径
	AssetRegistryModule.Get().GetAssets(Filter, BlueprintList); // 获取资源

	return BlueprintList; // 返回蓝图列表
}

/**
 * 查找蓝图类
 */
UClass* ULyraDevelopmentStatics::FindBlueprintClass(const FString& TargetNameRaw, UClass* DesiredBaseClass)
{
	FString TargetName = TargetNameRaw; // 目标名称
	TargetName.RemoveFromEnd(TEXT("_C"), ESearchCase::CaseSensitive); // 移除末尾的"_C"

	TArray<FAssetData> BlueprintList = ULyraDevelopmentStatics::GetAllBlueprints(); // 获取所有蓝图
	for (const FAssetData& AssetData : BlueprintList) // 遍历蓝图列表
	{
		if ((AssetData.AssetName.ToString() == TargetName) || (AssetData.GetObjectPathString() == TargetName)) // 检查名称匹配
		{
			if (UBlueprint* BP = Cast<UBlueprint>(AssetData.GetAsset())) // 转换为蓝图
			{
				if (UClass* GeneratedClass = BP->GeneratedClass) // 获取生成的类
				{
					if (GeneratedClass->IsChildOf(DesiredBaseClass)) // 检查是否是所需基类的子类
					{
						return GeneratedClass; // 返回生成的类
					}
				}
			}
		}
	}

	return nullptr; // 返回空指针
}

/**
 * 通过短名称查找类
 */
UClass* ULyraDevelopmentStatics::FindClassByShortName(const FString& SearchToken, UClass* DesiredBaseClass, bool bLogFailures)
{
	check(DesiredBaseClass); // 检查所需基类是否有效

	FString TargetName = SearchToken; // 目标名称

	// 在诉诸资源注册表之前，首先检查本地类和已加载的资源
	bool bIsValidClassName = true; // 是否为有效类名
	if (TargetName.IsEmpty() || TargetName.Contains(TEXT(" "))) // 检查是否为空或包含空格
	{
		bIsValidClassName = false; // 无效类名
	}
	else if (!FPackageName::IsShortPackageName(TargetName)) // 检查是否为短包名
	{
		if (TargetName.Contains(TEXT("."))) // 检查是否包含点
		{
			// 将type'path'转换为仅路径（如果不包含'，将返回完整字符串）
			TargetName = FPackageName::ExportTextPathToObjectPath(TargetName); // 转换导出文本路径为对象路径

			FString PackageName; // 包名
			FString ObjectName; // 对象名
			TargetName.Split(TEXT("."), &PackageName, &ObjectName); // 分割包名和对象名

			const bool bIncludeReadOnlyRoots = true; // 包括只读根
			FText Reason; // 原因
			if (!FPackageName::IsValidLongPackageName(PackageName, bIncludeReadOnlyRoots, &Reason)) // 检查是否为有效长包名
			{
				bIsValidClassName = false; // 无效类名
			}
		}
		else
		{
			bIsValidClassName = false; // 无效类名
		}
	}

	UClass* ResultClass = nullptr; // 结果类
	if (bIsValidClassName) // 如果是有效类名
	{
		ResultClass = UClass::TryFindTypeSlow<UClass>(TargetName); // 尝试查找类
	}

	// 如果我们还没有找到任何东西，尝试在资源注册表中查找符合要求的蓝图
	if (ResultClass == nullptr) // 如果结果类为空
	{
		ResultClass = FindBlueprintClass(TargetName, DesiredBaseClass); // 查找蓝图类
	}

	// 现在验证类（如果我们有一个）
	if (ResultClass != nullptr) // 如果结果类不为空
	{
		if (!ResultClass->IsChildOf(DesiredBaseClass)) // 检查是否是所需基类的子类
		{
			if (bLogFailures) // 如果记录失败
			{
				UE_LOG(LogConsoleResponse, Warning, TEXT("找到了资源 %s 但它不是类型 %s"), *ResultClass->GetPathName(), *DesiredBaseClass->GetName()); // 记录警告
			}
			ResultClass = nullptr; // 重置结果类
		}
	}
	else
	{
		if (bLogFailures) // 如果记录失败
		{
			UE_LOG(LogConsoleResponse, Warning, TEXT("找不到类型 %s 名为 %s 的类"), *DesiredBaseClass->GetName(), *SearchToken); // 记录警告
		}
	}

	return ResultClass; // 返回结果类
}